package com.it.you.base.designmode.singleton;


public class LanHanSingle {

    private static LanHanSingle INSTANCE;

    private LanHanSingle() {
    }

    //判断为null时实例化对象 但是在多线程环境下会创建多个 LanHanSingle对象
    public static LanHanSingle getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new LanHanSingle();
        }
        return INSTANCE;
    }

    //加锁 可以防止创建多个LanHanSingle对象，但下面写法，性能不是最优
    public synchronized static LanHanSingle getInstance2() {
        if (INSTANCE == null) {
            INSTANCE = new LanHanSingle();
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        System.out.println(LanHanSingle.getInstance().hashCode());
        System.out.println(LanHanSingle.getInstance().hashCode());
    }

    //在getInstance2()方法上进行性能优化 防止资源浪费or重复创建对象
    //为什么在加锁之后，还需要判断INSTANCE是否为空呢？
    //答：是为了防止在多线程并发的情况下，只会实例化一个对象。
    //比如：线程a和线程b同时调用getInstance方法，假如同时判断INSTANCE都为空，这时会同时进行抢锁。
    //假如线程a先抢到锁，开始执行synchronized关键字包含的代码，此时线程b处于等待状态。
    //线程a创建完新实例了，释放锁了，此时线程b拿到锁，进入synchronized关键字包含的代码，
    //如果没有再判断一次INSTANCE是否为空，则可能会重复创建实例。
    //所以需要在synchronized前后两次判断。

    //volatile关键字可以保证多个线程的可见性，但是不能保证原子性。同时它也能禁止指令重排。
    private volatile static LanHanSingle INSTANCE_2;
    public static LanHanSingle getInstance3() {
        if (INSTANCE_2 == null) {
            synchronized (LanHanSingle.class) {
                if (INSTANCE_2 == null) {
                    INSTANCE_2 = new LanHanSingle();
                }
            }
        }
        return INSTANCE_2;
    }

}
